import { Injectable } from "@nestjs/common";
import { JwtService } from "@nestjs/jwt";
import { SharedService } from "src/shared/shared.service";
import { InjectRedis, Redis } from "@nestjs-modules/ioredis";
import {
  USER_DEPTID_KEY,
  USER_DEPTNAME_KEY,
  USER_NICKNAME_KEY,
  USER_PERMISSIONS_KEY,
  USER_ROLEKEYS_KEY,
  USER_ROLES_KEY,
  USER_TOKEN_KEY,
  USER_USERNAME_KEY,
  USER_VERSION_KEY,
} from "src/common/constants/redis.constant";
import { Request } from "express";
import { UserService } from "../system/user/user.service";
import { User } from "../system/user/entities/user.entity";
import { MenuService } from "../system/menu/menu.service";
import { ResInfo } from "./dto/res-login.dto";
import { ApiException } from "src/common/exceptions/api.exception";
import { LogService } from "../monitor/log/log.service";

@Injectable()
export class LoginService {
  constructor(
    @InjectRedis() private readonly redis: Redis,
    private readonly sharedService: SharedService,
    private readonly jwtService: JwtService,
    private readonly userService: UserService,
    private readonly menuService: MenuService,
    private readonly logService: LogService,
  ) {}

  /**
   * @description: 登录
   * @param {Request} request
   * @return {*}
   */
  async login(request: Request) {
    const { user } = request as any;
    const payload: Payload = { userId: user.userId, pv: 1 };
    // 生成token
    let jwtSign = this.jwtService.sign(payload);

    const token = await this.redis.get(`${USER_TOKEN_KEY}:${user.userId}`);
    if (token) {
      jwtSign = token;
    }
    //存储密码版本号，防止登录期间 密码被管理员更改后 还能继续登录
    await this.redis.set(`${USER_VERSION_KEY}:${user.userId}`, 1);
    //存储token, 防止重复登录问题，设置token过期时间(1天后 token 自动过期)，以及主动注销token。
    await this.redis.set(`${USER_TOKEN_KEY}:${user.userId}`, jwtSign, "EX", 60 * 60 * 24);
    //调用存储在线用户接口
    await this.logService.addLogininfor(request, "登录成功", `${USER_TOKEN_KEY}:${user.userId}`);
    return { token: jwtSign };
  }

  // 退出登录
  async logout(token: string) {
    try {
      const payload = this.jwtService.verify(token);
      if (await this.redis.get(`${USER_TOKEN_KEY}:${payload.userId}`)) {
        await this.redis.del(`${USER_TOKEN_KEY}:${payload.userId}`);
      }
    } catch (err) {
      console.log(err);
    }
  }

  /**
   * @description: 获取用户信息
   * @param {number} userId
   * @return {*}
   */
  async getInfo(userId: number): Promise<ResInfo> {
    const user: User = await this.userService.findOneUserAllById(userId);
    if (!user) throw new ApiException("用户信息已被修改", 401);
    const dept_id = user.dept ? user.dept.dept_id : "";
    const dept_name = user.dept ? user.dept.dept_name : "";
    let roleKeyArr: string[] = user.roles.map((role) => role.role_key);
    let permissions = [];
    if (!roleKeyArr.length) {
      permissions = [];
    } else {
      // 权限待完善
    }

    // 将用户信息，权限数组，角色数组，存放进入缓存
    const promiseArr = [
      this.redis.set(`${USER_USERNAME_KEY}:${userId}`, user.username),
      this.redis.set(`${USER_NICKNAME_KEY}:${userId}`, user.nick_name),
      this.redis.set(`${USER_DEPTID_KEY}:${userId}`, dept_id),
      this.redis.set(`${USER_DEPTNAME_KEY}:${userId}`, dept_name),
      this.redis.set(`${USER_PERMISSIONS_KEY}:${userId}`, JSON.stringify(permissions)),
      this.redis.set(`${USER_ROLEKEYS_KEY}:${userId}`, JSON.stringify(roleKeyArr)),
      this.redis.set(`${USER_ROLES_KEY}:${userId}`, JSON.stringify(user.roles)),
    ];
    await Promise.all(promiseArr);
    return {
      permissions,
      roles: roleKeyArr,
      user,
    };
  }

  /**
   * @description: 获取当前用户的菜单
   * @param {number} userId
   * @return {*}
   */
  async getRouterByUser(userId: number) {
    const user: User = await this.userService.findOneUserAllById(userId);
    const isAdmin = user.roles.some((role) => role.role_key === "admin");
    const roleIdArr = user.roles.map((role) => role.role_id);
    if (!isAdmin && !roleIdArr.length) return [];
    return await this.menuService.getMenuList(isAdmin, roleIdArr);
  }
}
